import os
import json
import time
import decimal
import collections

from common.logger import log
from utils.utils import Utils
from common.benchmark_var import StatusCode, GlobalEnv, ConfigOptions as ConfigOps, MachineOptions as MachineOps,\
    ConfigSections as Sections, GlobalVar
from framework.report.var_report import JSONFileKey as Key, ReportContentKey as ReportKey
from common.machine_info import HwSwInfo

HARDWARE_LIST = [ConfigOps.PRODUCT_MODEL, MachineOps.BIOS_VERSION, MachineOps.ARCH, ConfigOps.CPU_MODEL,
                 MachineOps.CPU_MAX_MHZ, MachineOps.CPU_NOMINAL_MHZ, MachineOps.CPU_BOGOMIPS, MachineOps.SOCKETS,
                 MachineOps.CPU_CORES, MachineOps.CPU_LOGIC_CORES, MachineOps.L1D_CACHE, MachineOps.L1I_CACHE,
                 MachineOps.L2_CACHE, MachineOps.L3_CACHE, MachineOps.MEMORY_TOTAL, MachineOps.MEMORY_COUNT,
                 MachineOps.MEMORY_CAPACITY, MachineOps.MEMORY_MANU, MachineOps.MEMORY_TYPE, MachineOps.MEMORY_SPEED,
                 MachineOps.OS_DISK, MachineOps.DISK, ConfigOps.NETCARDS, ConfigOps.RAID, ConfigOps.DISPLAY_CARDS,
                 ConfigOps.MANUFACTURER, ConfigOps.HARDWARE_OTHERS]

SOFTWARE_LIST = [ConfigOps.OS, MachineOps.KERNEL, MachineOps.PAGE_SIZE, MachineOps.HUGE_PAGE_SIZE, MachineOps.THP,
                 MachineOps.RUN_LEVEL, MachineOps.BASH, MachineOps.GCC, MachineOps.GXX, MachineOps.BINUTILS,
                 MachineOps.PYTHON3, MachineOps.JAVA, MachineOps.GLIBC, MachineOps.FILE_SYSTEM, MachineOps.MOUNT_PARAM,
                 MachineOps.IO_SCHEDULER, ConfigOps.SOFTWARE_OTHERS]


class DecimalTrans(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, decimal.Decimal):
            return float(obj)
        super(DecimalTrans, self).default(obj)


class JsonMaker:
    def __init__(self, options, result, machine_info, run_time):
        self.options = options.get(Sections.GENERAL)
        self.result = result
        self.machine_info = machine_info
        self.run_time = run_time
        self.json_file = None
        self.data_dict = collections.OrderedDict()

    def make_json_file(self):
        try:
            self.data_dict[Key.SYSTEM] = collections.OrderedDict()
            self.data_dict[Key.BM_CONTENT] = collections.OrderedDict()
            self.write_machine_info()
            self.write_bench_summary()
            self.data_dict[Key.BM_CONTENT][Key.BM_DETAIL] = self.result
            data_json = json.dumps(self.data_dict, cls=DecimalTrans, ensure_ascii=False, indent=4)
            self.json_file = self.get_json_file()
            with open(self.json_file, "w") as file:
                file.write(data_json)
            #log.debug("The json file is %s" % self.json_file)
            return self.data_dict
        except Exception as err:
            log.error("Make json file failed, %s" % err)
            return dict()

    def write_machine_info(self):
        try:
            log.info("Start write machine info.")
            hw_info = collections.OrderedDict()
            sw_info = collections.OrderedDict()

            for hardware in HARDWARE_LIST:
                hw_info[hardware] = self.options.get(hardware, "")
            self.data_dict[Key.SYSTEM][Key.HARDWARE] = hw_info

            for software in SOFTWARE_LIST:
                sw_info[software] = self.options.get(software, "")
            self.data_dict[Key.SYSTEM][Key.SOFTWARE] = sw_info

            system_info = {ReportKey.HARDWARE: self.data_dict[Key.SYSTEM][Key.HARDWARE],
                           ReportKey.SOFTWARE: self.data_dict[Key.SYSTEM][Key.SOFTWARE]}
            update_info = HwSwInfo.get_hw_and_sw(system_info, self.machine_info)
            self.data_dict[Key.SYSTEM][Key.HARDWARE] = update_info.get(ReportKey.HARDWARE)
            self.data_dict[Key.SYSTEM][Key.SOFTWARE] = update_info.get(ReportKey.SOFTWARE)

        except (TypeError, AttributeError, KeyError) as err:
            log.error("Write machine info failed, %s", err)

    def write_bench_summary(self):
        try:
            log.info("Start writing benchmark summary.")
            bench_summary = collections.OrderedDict()
            bench_summary[Key.MODE] = self.options.get(ConfigOps.MODE)
            bench_summary[Key.VERSION] = GlobalVar.VERSION
            bench_summary[Key.OUTPUT_DIR] = self.options.get(ConfigOps.OUTPUT_DIR)
            bench_summary[Key.ITERATIONS] = self.options.get(ConfigOps.ITERATIONS)
            bench_summary[Key.COPIES] = self.options.get(ConfigOps.COPIES)
            bench_summary[Key.SKIP_FAIL] = self.options.get(ConfigOps.SKIP_FAIL)
            bench_summary[Key.SKIP_VERIFY] = self.options.get(ConfigOps.SKIP_VERIFY)
            bench_summary[Key.SCORE] = self.get_total_score()
            bench_summary[Key.TESTER_NAME] = self.options.get(ConfigOps.TESTER_NAME)
            bench_summary[Key.TEST_TIME] = self.options.get(ConfigOps.TEST_DATE)
            bench_summary[Key.RUN_TIME] = self.run_time
            bench_summary[Key.REPORT_TIME] = time.time()
            bench_summary[Key.CONFIG] = self.get_encode_data(self.options)
            bench_summary[Key.MACHINE_INFO] = self.get_encode_data(self.machine_info)
            self.data_dict[Key.BM_CONTENT][Key.BM_SUMMARY] = bench_summary

        except AttributeError as err:
            log.error("Write benchmark summary failed, %s" % err)

    def get_total_score(self):
        mode = self.options.get(ConfigOps.MODE)
        if len(self.result) == 1 and self.result[0].get(Key.NAME) == "{}Suite".format(mode):
            return self.result[0].get(Key.SCORE)
        return ""

    @staticmethod
    def get_encode_data(data):
        try:
            res = Utils.b64encode(str(data))
            if res[0] == StatusCode.SUCCESS and res[1] > 0:
                return res[2]
        except Exception as err:
            log.error("Encode failed, %s" % err)

    def get_json_file(self):
        try:
            file_path = self.options.get(ConfigOps.OUTPUT_DIR)
            name_list = [GlobalEnv.TOOL_NAME]
            capacity = Key.SINGLE if self.options.get(ConfigOps.COPIES) == 1 else Key.MULTI
            name_list.append(capacity)
            time_str = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
            name_list.append(time_str)
            file_name = "{}.json".format("_".join(name_list))
            json_file = os.path.join(file_path, file_name)
            return json_file
        except Exception as err:
            log.error("Get json file failed, %s" % err)
            return ""
